using System;
using System.Xml;
using System.Web;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (Pty) ltd
	Author:		Alan Benington	
	Started:	2005-05-15	
	Status:		release	
	Version:	2.0.0
	Buidl:		20071103
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/

/*	-----------------------------------------------------------------------	
	Development Notes:
	==================
	20070524:	Modification for .Net 2.0
	20071103:	Added UpdateFormXml method (similar to UpdateProfile) and 
				FormXml for adding formfields as xml to profile
	-----------------------------------------------------------------------	*/

namespace umlungu.engineX.webX {
	/// <summary>
	/// umlungu engineX web framework, profile class.
	///
	/// The profile class has the following purpose/function:
	///	- saves, adds and retrieves profile values
	///	- 
	/// Note:
	/// - This has been adapted from nm_formUtils and the nm profile handling.
	/// - Some future refinements:
	///		- rework the xml structure (including values saved in fld's)
	///		- rationalise some of the methods (not much changed from org)
	/// </summary>
	public class profileX {
		/// <summary>
		/// Local objects and properties
		/// </summary>
		private System.Web.UI.Page page;
		private string siteid;
		private XmlDocument xml;
		private XmlElement identity;
		private XmlElement formxml;
		private XmlElement xmldoc;
		private bool debug;
		private const string FormXmlExclusions = "id|Title|BlogSpaces|Topics|Text";
		/// <summary>
		/// Public properties
		/// </summary>
		public XmlElement Xmldoc	{	get	{ return xmldoc; }	set	{ xmldoc = value; }}
		public XmlDocument Xml		{	get	{ return xml; }}
		/// <summary>
		/// Accessors
		/// </summary>
		public string Group			{	get { return(identity.GetAttribute("group")); }	set { identity.SetAttribute("group", value);	}}
		public string Token			{	get { return(identity.GetAttribute("token")); }	set { identity.SetAttribute("token", value);	}}
		
		public XmlElement Identity	{	get { return(identity); }}
		public string Name			{	get	{ return(identity.SelectSingleNode("name").InnerText); }		set { identity.SelectSingleNode("name").InnerText = value; }}
		public string Username		{	get	{ return(identity.SelectSingleNode("username").InnerText); }	set { identity.SelectSingleNode("username").InnerText = value; }}
		public string Firstname		{	get	{ return(identity.SelectSingleNode("firstname").InnerText); }	set { identity.SelectSingleNode("firstname").InnerText = value; }}
		public string Surname		{	get	{ return(identity.SelectSingleNode("surname").InnerText); }		set { identity.SelectSingleNode("surname").InnerText = value; }}
		public string Email			{	get	{ return(identity.SelectSingleNode("email").InnerText); }		set { identity.SelectSingleNode("email").InnerText = value; }}
		public string Telno			{	get	{ return(identity.SelectSingleNode("telno").InnerText); }		set { identity.SelectSingleNode("telno").InnerText = value; }}

		public XmlElement FormXml	{ get { return (formxml); } }

		#region Public methods
		/// <summary>Constructor</summary>
		public profileX(System.Web.UI.Page thispage, bool wantdebug, string thissite) {
			siteid = thissite;
			page = thispage;
			debug = wantdebug;
			initialise(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public void Reset() {
			initialise(true);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public string GetProfileValue(string prfname) {
			if (xml == null)
				return("");
			XmlNode prfnode = xml.DocumentElement.SelectSingleNode(String.Concat("//fld[@name='", prfname, "']/value"));
			return((prfnode != null)? prfnode.InnerText : "");
		}
		/// <summary>
		/// Method description
		/// </summary>
		public XmlNode GetNode(string profname, string nodename) {
			if (xml == null)
				return(null);
			XmlNode	profnode = xml.DocumentElement.SelectSingleNode(String.Concat("//", profname));
			return((profnode != null)? profnode.SelectSingleNode(nodename) : null);
		}
		/// <summary>
		/// Method description
		/// </summary>
		// NB this has been changed from the version in NM to add a node to the profile anywhere,
		//		not just on a fields/fld node in the profile
		//	where:
		//		profname:		string, name of profile node to add to
		//		newnode:		DOMNode, the node to be added to the profile
		//		want_replace:	boolean, true = replace old (if exists) with new.
		public bool AddNodetoProfile(string profname, XmlNode newnode, bool want_replace) {
			_Debug("AddNodetoProfile", "profname", profname);
			if (xml == null)
				return(false);
			XmlNode	profnode = xml.DocumentElement.SelectSingleNode(String.Concat("//", profname));
			if (profnode != null) {
				string newname = newnode.Name;
				XmlNode prfnode = profnode.SelectSingleNode(newname);
				if (prfnode != null && want_replace)	// if it exists, remove the old one first
					profnode.RemoveChild(prfnode);
				//profnode.AppendChild(newnode);	// original
				profnode.InnerXml = String.Concat(profnode.InnerXml, newnode.OuterXml);
				writeDebug();
				return(true);
			}
			else
				return(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public XmlElement addElemtoProfile(string profname, string name, string value ) {
			if (xml == null)
				return(null);
			XmlNode	profnode = xml.DocumentElement.SelectSingleNode(String.Concat("//", profname));
			if (profnode != null) {
				XmlElement elem = (XmlElement) profnode.AppendChild(xml.CreateElement(name));
				elem.InnerText = value;
				return(elem);
			}
			else
				return(null);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool RemoveNodefromProfile(string profname) {
			_Debug("RemoveNodefromProfile", "profname:", profname);
			if (xml == null)
				return(false);
			XmlNode profnode = xml.DocumentElement.SelectSingleNode(String.Concat("//", profname));
			if (profnode != null) {
				profnode.ParentNode.RemoveChild(profnode);
				writeDebug();
				return(true);
			}
			else
				return(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool ResetNodeinProfile(string profname, string nodename) {
			if (xml == null)
				return(false);
			XmlNode profnode = xml.DocumentElement.SelectSingleNode(String.Concat("//", profname));
			if (profnode != null) {
				XmlNode prfnode = profnode.SelectSingleNode(nodename);
				if (prfnode != null)	// if it exists, remove the old one first
					profnode.RemoveChild(prfnode);
				addElem(profnode, nodename, "");
				writeDebug();
				return(true);
			}
			else
				return(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool AddFldtoProfile(string fldname, string fldvalue) {
			if (xml == null)
				return(false);
		
			XmlNode	fldsnode = xml.DocumentElement.SelectSingleNode("//fields");
			if (fldsnode != null) {
				XmlElement fldnode = (XmlElement) fldsnode.SelectSingleNode(String.Concat("fld[@name='", fldname, "']/value"));
				if (fldnode != null)	// if it exists add the value
					fldnode.InnerText = fldvalue;
				else {	// doesnt exist - check for fld parent
					fldnode = (XmlElement) fldsnode.SelectSingleNode(String.Concat("fld[@name='", fldname, "']"));
					if (fldnode == null)	// doesnt exist - add the element
						fldnode = (XmlElement) addElemAttr(fldsnode, "fld", "", "name", fldname);
					if (fldnode != null)
						addElem(fldnode, "value", fldvalue);
					else
						return(false);
				}
				writeDebug();
				return(true);
			}
			else
				return(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool AddSubobjtoFld(string fldname, string subname, string subvalue) {
			if (xml == null)
				return(false);
			
			XmlNode	fldsnode = xml.DocumentElement.SelectSingleNode("//fields");
			if (fldsnode != null) {
				XmlElement fldnode = (XmlElement) fldsnode.SelectSingleNode(String.Concat("fld[@name='", fldname, "']"));
				if (fldnode == null)	// doesnt exist - add the element
					fldnode = (XmlElement) addElemAttr(fldsnode, "fld", "", "name", fldname);
				if (fldnode != null){
					XmlElement subsnode = (XmlElement) fldnode.SelectSingleNode("subobjs");
					if (subsnode == null)
						subsnode = (XmlElement) addElem(fldnode, "subobjs", "");
					if (subsnode != null) {
						XmlElement subnode = (XmlElement) subsnode.SelectSingleNode(String.Concat("subwf_obj[value='", subvalue, "']"));
						if (subnode == null) {
							subnode = (XmlElement) addElem(subsnode, "subwf_obj", "");
							addElem(subnode, "value", subvalue);
							addElem(subnode, "prompt", subname);
						}
						if (subnode != null) {
							subnode.SelectSingleNode("value").InnerText = subvalue;
							subnode.SelectSingleNode("prompt").InnerText = subname;
						}
						else
							return(false);
					}
					else
						return(false);
				}
				else
					return(false);
				return(true);
			}
			else
				return(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool ClearSubobjfromFld(string fldname) {
			if (xml == null)
				return(false);
			
			XmlNode	fldsnode = xml.DocumentElement.SelectSingleNode("//fields");
			if (fldsnode != null) {
				XmlNode fldnode = fldsnode.SelectSingleNode(String.Concat("fld[@name='", fldname, "']"));
				if (fldnode != null){
					XmlNode subsnode = fldnode.SelectSingleNode("subobjs");
					if (subsnode != null)
						fldnode.RemoveChild(subsnode);
				}
				else
					return(false);
				return(true);
			}
			else
				return(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool AddFldNodetoProfile(string fldname, XmlNode newnode, bool want_replace) {
			if (xml == null)
				return(false);
		
			XmlNode profnode = xml.DocumentElement.SelectSingleNode("//fields");
			if (profnode != null) {
				XmlNode	prfnode = profnode.SelectSingleNode(String.Concat("fld[@name='", fldname, "']"));
				if (prfnode != null && want_replace)	// if it exists, remove the old one first
					profnode.RemoveChild(prfnode);
				profnode.AppendChild(newnode);
				writeDebug();
				return(true);
			}
			else
				return(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool AddtoProfile(XmlDocument xmldoc, string nodename) {
			if (xml == null)
				return(false);
		
			XmlNode prfnode, fldsnode = xml.DocumentElement.SelectSingleNode("//fields");
			XmlNode docnode = xmldoc.DocumentElement.SelectSingleNode(String.Concat("//", nodename));	// eg: nodename = 'Transaction_Result'
			XmlNodeList docnodeList = docnode.ChildNodes;
			foreach(XmlNode thisnode in docnodeList) {
				prfnode = fldsnode.SelectSingleNode(String.Concat("fld[@name='", thisnode.Name, "']/value"));
				if (prfnode != null)	// if it exists add the value
					prfnode.InnerText = thisnode.InnerText;
			}
			writeDebug();
			return(true);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool AddNewNodetoProfile(XmlDocument xmldoc, string nodename) {
			if (xml == null)
				return(false);
			
			XmlNode docnode = xmldoc.DocumentElement.SelectSingleNode(String.Concat("//", nodename));	// eg: nodename = 'Transaction_Result'
			XmlNodeList docnodeList = docnode.ChildNodes;
			foreach(XmlNode thisnode in docnodeList)
				AddProfileValue(thisnode.Name, thisnode.InnerText);
			writeDebug();
			return(true);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool AddDoctoProfile(XmlDocument xmldoc, XmlNode selectedNode, string newnodeString) {
			if (xml == null)
				return(false);
			// locate the custdata node and check no existing node (delete if is)
			XmlNode custdatanode = xml.DocumentElement.SelectSingleNode("//custdata");
			XmlElement newNode = (XmlElement) custdatanode.SelectSingleNode(newnodeString);
			if (newNode != null)	// remove this node if it exists
				custdatanode.RemoveChild(newNode);
			// locate the node to copy in the doc and add to the custdata node in profile
			XmlNode docnode = xmldoc.DocumentElement.SelectSingleNode(String.Concat("//", selectedNode));
			newNode = xmldoc.CreateElement(newnodeString);
			XmlNode addNode = custdatanode.AppendChild(newNode);
			addNode.InnerXml = docnode.OuterXml;
		
			writeDebug();
			return(true);
		}
		/// <summary>Update user profile from form fields and query string parameters</summary>
		public bool UpdateProfile(bool add) {
			_Debug("UpdateProfile", "add:", add.ToString());
			if (xml == null)
				return(false);
			// Add form fields to profile
			XmlNode	fldsnode = xml.DocumentElement.SelectSingleNode("//fields");
			string thisfld, thisname;
			_Debug("UpdateProfile", "Request.Form.Count:", page.Request.Form.Count.ToString());
			for (int i=0; i < page.Request.Form.Count; i++) { 
				thisfld = page.Request.Form.Get(i);
				thisname = page.Request.Form.GetKey(i);
				XmlNode prfnode = fldsnode.SelectSingleNode(String.Concat("fld[@name='", thisname, "']/value"));
				if (prfnode != null)	// if it exists add the value
					prfnode.InnerText = thisfld;
				else if (add) {	// add the field if it doesn't exist: <fld name=""><value/></fld>
					XmlElement newfld = addElemAttr(fldsnode, "fld", "", "name", thisname);
					addElem(newfld, "value", thisfld);
				}
			}
			// Now do the same for url parameters
			string  notstr = "link|form|service|process";
			_Debug("UpdateProfile", "Request.QueryString.Count:", page.Request.QueryString.Count.ToString());
			for (int i=0; i < page.Request.QueryString.Count; i++) { 
				thisfld = page.Request.QueryString.Get(i);
				thisname = page.Request.QueryString.GetKey(i);
				if (notstr.IndexOf(thisname) < 0) {
					XmlNode prfnode = fldsnode.SelectSingleNode(String.Concat("fld[@name='", thisname, "']/value"));
					if (prfnode != null)	// if it exists add the value
						prfnode.InnerText = thisfld;
					else if (add) {	// add the field if it doesn't exist: <fld name=""><value/></fld>
						XmlElement newfld = addElemAttr(fldsnode, "fld", "", "name", thisname);
						addElem(newfld, "value", thisfld);
					}
				}
			}
			writeDebug();
			return(true);
		}
		/// <summary>Update profile form xml from form fields</summary>
		public bool UpdateFormXml(string type) {
			if (type == "")
				return true;
			else
				return UpdateFormXml((type == "xhtml"));
		}
		/// <summary>Update profile form xml from form fields</summary>
		public bool UpdateFormXml(bool isXhtml) {
			const string FORMAT_XML = "<{0}>{1}</{0}>";
			const string FORMAT_XHTML = "<{0} type='html'><![CDATA[{1}]]></{0}>";

			_Debug("UpdateFormXml", "begin:");
			if (xml == null)
				return (false);
			if (formxml == null)
				return (false);
			// Add form fields to profile
			formxml.RemoveAll();
			string thisfld, thisname, thisxml = "";
			_Debug("UpdateFormXml", "Request.Form.Count:", page.Request.Form.Count.ToString());
			for (int i = 0; i < page.Request.Form.Count; i++) {
				thisfld = page.Request.Form.Get(i);
				thisname = page.Request.Form.GetKey(i);
				if (FormXmlExclusions.IndexOf(thisname) == -1) {
					string fldxml = String.Format((isXhtml)? FORMAT_XHTML : FORMAT_XML, thisname, htmlIn(thisfld));
					thisxml = String.Concat(thisxml, fldxml);
				}
			}
			_Debug("UpdateFormXml", "thisxml:", thisxml);
			formxml.InnerXml = thisxml;
			writeDebug();
			return (true);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool AddProfileValue(string prfname, XmlNode prfnode) {
			return(AddProfileValue(prfname, prfnode.InnerText));
			}
		public bool AddProfileValue(string prfname, string prfvalue) {
			if (xml == null)
				return(false);
		
			XmlNode	fldsnode = xml.DocumentElement.SelectSingleNode("//fields");
			if (fldsnode != null) {
				XmlNode prfnode = fldsnode.SelectSingleNode(String.Concat("fld[@name='", prfname, "']/value"));
				if (prfnode != null)	// if it exists add the value
					prfnode.InnerText = prfvalue;
				else {	// doesnt exist - add the element
					XmlElement newfld = addElemAttr(fldsnode, "fld", "", "name", prfname);
					addElem(newfld, "value", prfvalue);
				}
				writeDebug();
				return(true);
			}
			else
				return(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		public bool ClearProfileValue(string prfname) {
			if (xml == null)
				return(false);
		
			XmlNode	fldsnode = xml.DocumentElement.SelectSingleNode("//fields");
			if (fldsnode != null) {
				XmlNode prfnode = fldsnode.SelectSingleNode(String.Concat("fld[@name='" + prfname + "']/value"));
				if (prfnode != null)	// if it exists set to empty
					prfnode.InnerText = "";
				writeDebug();
				return(true);
				}
			else
				return(false);
		}
		/// <summary>
		/// Method description
		/// </summary>
		// Old functions (from NM) - possibly redundant, but keep for now	
		public bool old_AddFldtoProfile(string fldname, string fldvalue) {
			if (xml == null)
				return(false);
		
			XmlNode fldsnode = xml.DocumentElement.SelectSingleNode("//fields");
			XmlElement newfld = (XmlElement) fldsnode.AppendChild(xml.CreateElement("fld"));
			XmlElement valelem = (XmlElement) newfld.AppendChild(xml.CreateElement("value"));
			valelem.InnerText = fldvalue;
			newfld.SetAttribute("name", fldname);
		
			writeDebug();
			return(true);
		}
		#endregion

		#region Protected methods
		/// <summary>
		/// Method description
		/// </summary>
		protected void _Debug(string label, params string[] outparam) {
			if (debug) {
				page.Response.Write("\n<!-- profileX.");
				page.Response.Write(label);
				page.Response.Write("::\n");
				for (int i=0; i < outparam.Length; i++)
					page.Response.Write(outparam[i]);
				page.Response.Write("\n-->\n");
				page.Response.Flush();
			}
		}
		#endregion

		#region Private methods
		/// <summary>
		/// Method description
		/// </summary>
		private void initialise(bool reset) {
			string profxml = String.Concat(siteid, "_", "x_profile_xml");
			_Debug("initialise", "reset:", reset.ToString(), " profxml:", profxml);
			if (reset || page.Session[profxml] == null) {
				_Debug("initialise", "reset/start:");
				XmlDocument prfxml = (XmlDocument) page.Application[profxml];
				xml = new XmlDocument();
				xml.LoadXml(prfxml.OuterXml);
				init();
				page.Session[profxml] = prfxml;
			} else {
				_Debug("initialise", "re-load:");
				xml = (XmlDocument) page.Session[profxml];
				init();
			}
		}
		private void init() {
			_Debug("init");
			xmldoc = xml.DocumentElement;
			identity = (XmlElement) xmldoc.SelectSingleNode("/profile/identity");
			formxml = (XmlElement)xmldoc.SelectSingleNode("/profile/formxml");
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void writeDebug() {
			if (debug)
				xml.Save(page.Server.MapPath("x_profile_dbg.xml"));
		}
		/// <summary>
		/// Method description
		/// </summary>
		private XmlElement updElem(string nodename, string elname, string elvalue ) {
			return(xmlX.UpdElem(xml, nodename, elname, elvalue));
		}
		private void addAttr(XmlNode node, string name, string value ) {
			xmlX.AddAttr(xml, node, name, value);
		}
		private XmlElement addElem(XmlNode node, string elname, string elvalue) {
			return(xmlX.AddElem(xml, node, elname, elvalue));
		}
		private XmlElement addElemAttr(XmlNode node, string elname, string elvalue, string atname, string atvalue) {
			return(xmlX.AddElemAttr(xml, node, elname, elvalue, atname, atvalue));
		}
		private XmlElement addElemAttrNode(XmlNode node, string elname, int which) {
			return(xmlX.AddElemAttrNode(xml, node, elname, which));
		}
		private string htmlIn(string htm) {
			htm = HttpUtility.HtmlEncode(htm);
			htm = htm.Replace(Environment.NewLine, "");
			htm = htm.Replace("\n", "");
			return (htm.Replace("%20", " "));
		}
		#endregion
	}
}
		
